namespace Ex
{
    using System.Collections;
    using System.Diagnostics;
    using System.Text;
    using KeraLua;

    public static class Tool
    {
        // public static void PushObjectToLuaStack(Lua lua, object obj, UsableLuaType type)
        // {
        //     switch (type)
        //     {
        //         case UsableLuaType.nil:
        //             lua.PushNil();
        //             break;
        //         case UsableLuaType.boolean:
        //             lua.PushBoolean((bool)obj);
        //             break;
        //         case UsableLuaType.number:
        //             switch (obj)
        //             {
        //                 case double d: lua.PushNumber(d); break;
        //                 case float f: lua.PushNumber(f); break;
        //                 case int i: lua.PushInteger(i); break;
        //                 case short i: lua.PushInteger(i); break;
        //                 default: lua.PushNumber((double)obj); break;
        //             }
        //             break;
        //         case UsableLuaType.luaString:
        //             lua.PushString((string)obj);
        //             break;
        //         case UsableLuaType.cfunction:
        //         case UsableLuaType.function:
        //         case UsableLuaType.table:
        //         case UsableLuaType.csobject:
        //             {
        //                 ((IExElement)obj).PushToLuaStack();
        //             }
        //             break;
        //         default:
        //             throw new InvalidDataException("unsupport object:" + obj);
        //     }
        // }

        // public static void PushObjectToLuaStack(Lua lua, object obj)
        // {
        //     PushObjectToLuaStack(lua, obj, GetCsToLuaType(obj));
        // }

        public static UsableLuaType GetCsToLuaType(object? value)
        {
            return value switch
            {
                //value也有可能是type值（值为nil）
                UsableLuaType lua_type => lua_type,
                null => UsableLuaType.nil,
                bool => UsableLuaType.boolean,
                double => UsableLuaType.number,
                float => UsableLuaType.number,
                int => UsableLuaType.number,
                string => UsableLuaType.luaString,
                IExLuaFunction => UsableLuaType.function,
                IExCsFunction => UsableLuaType.cfunction,
                IExTable => UsableLuaType.table,
                ExCsRawData => UsableLuaType.csobject,
                _ => throw new InvalidDataException("unsupported type:" + value)
            };
        }

        public static UsableLuaType GetTypeOnStack(Lua lua, int index)
        {
            if (lua.IsNil(index))
            {
                return UsableLuaType.nil;
            }
            if (lua.IsUserData(index))
            {
                return UsableLuaType.csobject;
            }
            if (lua.IsBoolean(index))
            {
                return UsableLuaType.boolean;
            }
            if (lua.IsNumber(index))
            {
                return UsableLuaType.number;
            }
            if (lua.IsString(index))
            {
                return UsableLuaType.luaString;
            }
            if (lua.IsCFunction(index))
            {
                return UsableLuaType.cfunction;
            }
            if (lua.IsFunction(index))
            {
                return UsableLuaType.function;
            }
            if (lua.IsTable(index))
            {
                return UsableLuaType.table;
            }
            return UsableLuaType.nil;
        }

        [Conditional("DEBUG")]
        public static void PrintTableTree(IExTable table)
        {
            int level = 0;
            Console.WriteLine("[Debug] table struct:");
            Console.Write(ShowTableTree(table, level));
        }

        private static string ShowTableTree(IExTable table, int level)
        {
            var it = table.GetEnumerator();
            var content = new StringBuilder();
            while (it.MoveNext())
            {
                if (level > 0)
                {
                    content.Append(string.Concat(Enumerable.Repeat("--", level)));
                }
                switch (GetCsToLuaType(it.Value))
                {
                    case UsableLuaType.nil:
                        content.AppendFormat("{0} : {1}", it.Key, "nil");
                        content.AppendLine();
                        break;
                    case UsableLuaType.boolean:
                        content.AppendFormat("{0} : {1}", it.Key, (bool)it.Value!);
                        content.AppendLine();
                        break;
                    case UsableLuaType.number:
                        content.AppendFormat("{0} : {1}", it.Key, Convert.ToDouble(it.Value!));
                        content.AppendLine();
                        break;
                    case UsableLuaType.cfunction:
                        content.AppendFormat("{0} : {1}", it.Key, it.Current!.ToString());
                        content.AppendLine();
                        break;
                    case UsableLuaType.function:
                        content.AppendFormat("{0} : {1}", it.Key, "luaFunction");
                        content.AppendLine();
                        break;
                    case UsableLuaType.luaString:
                        content.AppendFormat("{0} : {1}", it.Key, (string)it.Value!);
                        content.AppendLine();
                        break;
                    case UsableLuaType.table:
                        content.AppendFormat("{0} :", it.Key);
                        content.AppendLine();
                        content.Append(ShowTableTree((IExTable)it.Value!, level + 1));
                        break;
                    case UsableLuaType.csobject:
                        content.AppendFormat("{0} : {1}", it.Key, (ExCsRawData)it.Value!);
                        content.AppendLine();
                        break;
                    default:
                        content.AppendFormat("{0} : {1}", it.Key, "unknown");
                        content.AppendLine();
                        break;
                }
            }
            return content.ToString();
        }

        //获取堆栈信息
        // public static object WatchObjectFromLuaStack(Lua lua, UsableLuaType type, int index)
        // {
        //     return type switch
        //     {
        //         UsableLuaType.nil => UsableLuaType.nil,
        //         UsableLuaType.boolean => lua.ToBoolean(index),
        //         UsableLuaType.number => lua.ToNumber(index),
        //         UsableLuaType.luaString => lua.ToString(index),
        //         UsableLuaType.cfunction => ExCsFunctionRef.WatchFromLuaStack(lua, index),
        //         UsableLuaType.function => ExLuaFunction.WatchFromLuaStack(lua, index),
        //         UsableLuaType.table => ExRefTable.WatchFromLuaStack(lua, index),
        //         UsableLuaType.csobject => ExCsRawData.WatchFromLuaStack(lua, index),
        //         _ => throw new InvalidDataException("Invalid type:" + type.ToString()),
        //     };
        // }

        // //抓取堆栈最上层的值
        // public static object FetchObjectFromLuaStack(Lua lua, UsableLuaType type)
        // {
        //     object? result = null;
        //     switch (type)
        //     {
        //         case UsableLuaType.nil:
        //             result = UsableLuaType.nil;
        //             break;
        //         case UsableLuaType.boolean:
        //             result = lua.ToBoolean(-1);
        //             lua.Pop(1);
        //             break;
        //         case UsableLuaType.number:
        //             result = lua.ToNumber(-1);
        //             lua.Pop(1);
        //             break;
        //         case UsableLuaType.luaString:
        //             result = lua.ToString(-1);
        //             lua.Pop(1);
        //             break;
        //         case UsableLuaType.cfunction:
        //             result = ExCsFunctionRef.FetchFromLuaStack(lua);
        //             break;
        //         case UsableLuaType.function:
        //             result = ExLuaFunction.FetchFromLuaStack(lua);
        //             break;
        //         case UsableLuaType.table:
        //             result = ExRefTable.FetchFromLuaStack(lua);
        //             break;
        //         case UsableLuaType.csobject:
        //             result = ExCsRawData.FetchFromLuaStack(lua);
        //             break;
        //         default:
        //             throw new InvalidDataException("Invalid type:" + type);
        //     }
        //     return result;
        // }

        // public static object? FetchObjectFromLuaStack(Lua lua)
        // {
        //     return FetchObjectFromLuaStack(lua, GetTypeOnStack(lua, -1));
        // }

        // public static void CheckObjectIsGivenType(Lua lua, object obj, UsableLuaType type)
        // {
        //     if (GetCsToLuaType(obj) == type)
        //     {
        //         lua.Error(string.Format("Invalid type: should be {0}", type));
        //     }
        // }

        // Exception Editon
        public static void CheckObjectIsGivenType(EnvLua env, object obj, UsableLuaType type)
        {
            if (GetCsToLuaType(obj) != type)
            {
                env.Error(string.Format("Invalid type: should be {0}", type));
            }
        }

        public static IDictionary IExTableToDictionary(IExTable table)
        {
            switch (table)
            {
                case ExTable extable:
                    {
                        return extable.RawTable;
                    }
                default:
                    {
                        Dictionary<object, object> dic = new();
                        var enumerator = table.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            dic.Add(enumerator.Key, enumerator.Value!);
                        }
                        return dic;
                    }
            }
        }

        public static IDictionary<string, object> IExTableToStrDictionary(IExTable table)
        {
            Dictionary<string, object> dic = new();
            var enumerator = table.GetEnumerator();
            while (enumerator.MoveNext())
            {
                dic.Add(enumerator.Key is string str_key ? str_key : string.Concat(enumerator.Key.ToString(), '-', enumerator.Key.GetHashCode()), enumerator.Value!);
            }
            return dic;
        }
    }
}